#include <iostream>
#include <vector>
#include <set>

#include <time.h>
#include <math.h>

#include "Omega.h"
#include "Variable.h"
#include "Outcome.h"
#include "Preference.h"
#include "PreferenceSet.h"
#include "Constraint.h"

using namespace std;

vector< vector<int> > scpo(PreferenceSet P, Omega O);


int main(){
					cout<<"/-------------- TEST V2 ----------------/"<<endl<<endl;



	/* LES VARIABLES */

	Variable v1("M",2,"fish","meat"); 

	Variable v2("W",2,"white","red"); 

	Variable v3("D",2,"cake","ice_cream");
	cout<<endl;

	/* OMEGA */

	vector<Variable> vect; 
	vect.push_back(v1); vect.push_back(v2);	vect.push_back(v3); 
	Omega o(vect);

	/* PREFERENCES */

	Preference p1( "fish" , "meat") ;
	Preference p2( "meat", "fish", "red" ) ;
	Preference p3( "cake", "ice_cream", "white" ) ;
	p1.setSemantic("Optimist"); p2.setSemantic("Optimist"); p3.setSemantic("Optimist");
	p1.show(); p2.show(); p3.show();

	PreferenceSet ps;
	ps.addPreference(p1);
	ps.addPreference(p2);
	ps.addPreference(p3);
    cout<<endl;



			
	vector< vector<int> > res2 = scpo(ps,o);
	for(int i = 0;i<(int)res2.size();i++)
	{
		cout<<"E["<<i+1<<"] = {"<<endl;
        
		for(int j=0;j<(int)res2[i].size();j++)
		{   
			o.generateOutcome(res2[i][j]).show();
		}
		cout<<"}"<<endl;
	}
  





					cout<<endl<<"/-------------- /TEST V2 ----------------/"<<endl;

return 0;
}

vector< vector<int> > scpo(PreferenceSet P, Omega O)
{
	vector< vector<int> > result; // Contiendra le résultat

	O.generateIndex(); //On genere l'ensemble des Outcomes sous forme de numero

	int nbC = (int)(P.getAllPreferences()).size();
	vector<Constraint> C; 
	for(int i(0);i<nbC;++i){
		Constraint c(O,P.getPreference(i));
		C.push_back(c);
	} // Les contraintes 


	while(!O.getOmega()->empty()){
	
		/* 	Premiere etape
			On détecte les outcomes qui ne sont présentes dans aucune partie droite des contraintes
			Et on les ajoute a E
		*/

		vector<int> E;
		// On parcourt toutes les Outcomes de Omega avec un itérateur
		set<int>::iterator lit ((O.getOmega())->begin()), lend((O.getOmega())->end()); 
		for(;lit!=lend;++lit)
		{
			bool ok = true;
			int i(0);
			while( i<(int)C.size() && ok ){
				if( C[i].isInRight( *lit ) ) ok = false;
				i++;
			}
			if( ok  ){
				// Si on ne l'a croisé dans aucune contrainte (partie droite)
				// Et que l'on a vérifié chaque contrainte
				// On l'ajoute a l'ensemble E
				E.push_back( *lit );

			}
		}


		// Si E est vide, on arrete la fonction
		if((int)E.size() == 0)
		{
			cout<<"INCONSISTENT"<<endl;
			return result;
		}


		/* 	Deuxième étape
			On Supprime les contraintes qui sont satisfaites :
			- Strong et CP : Ci satisfaite si la partie gauche est vide 
			- Otpimist : Ci satisfaite si une outcome de E appartient a sa partie gauche
			On met a jour Omega et les contraintes restantes
		*/

        for (int j=0;j<E.size();j++)
		{
			// On met Omega à jour
			O.eraseOutcome(E[j]);

            for (int i((int)C.size()-1);i>=0;--i)
            {
				// On supprime les contraintes Optimist satisfaites
                if (C[i].getSemantic() == "Optimist")
                {
                    if (C[i].isInLeft(E[j])) C.erase(C.begin()+i);
                }
				// Et on met à jour les contraintes Strong et CP
				else
				{
					C[i].deleteOutcomeInLeft(E[j]);
					// Mise à jour des parties droites selon CP
					if (C[i].getSemantic() == "CeterisParibus"){
						int a = O.getCeterisParibus(E[j],P.getPreference(i),C[i].getRight());
						C[i].deleteOutcomeInRight(a);
					}
				}
            }
        }



		// On supprime les contraintes Strong et CP satisfaites
        for (int i((int)C.size()-1);i>=0;--i)
        {
			if (C[i].getSemantic() == "Strong" || C[i].getSemantic() == "CeterisParibus")
            {
				if( C[i].getSizeLeft() == 0 ) C.erase( C.begin() + i ) ;
			}
		}
		
		// On ajoute E au résultat
		result.push_back(E);
	}

	return result;

}
